import hashes
import common

NOSYMS_CHARSET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890"
NORMAL_CHARSET = NOSYMS_CHARSET + "~!@#$%^&*()-="
NUMBERS_ONLY_CHARSET = "0123456789"
LOWERCASE_ALPHA_CHARSET = "abcdefghijklmnopqrstuvwxyz"
UPPERCASE_ALPHA_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

class Transcoding:
    def transcode(self, buff, charset):
        divisor = len(charset)
        remainders = []
        
        if len(buff) % 2 != 0:
            buff += "\x00"
        
        dividend = [((ord(x)<<8) | ord(y)) for x, y in  zip(buff[::2], buff[1::2])]

        while dividend:
            quotient = []
            x = 0
            for i in dividend:
                q = ((x << 16) + i) / divisor #integer division
                x = ((x << 16) + i) % divisor
                if quotient or q:
                    quotient.append(q)
            remainders.append(x)
            dividend = quotient

        return "".join([charset[i] for i in remainders])

class GeneratorSettings:
    def __init__(self, modifier, age, length, hashName, charset):
        self.modifier = modifier
        self.age = age
        self.length = length
        self.hashName = hashName
        self.charset = charset
    def __str__(self):
        return "[ " + "\n".join(["%s = %r" % (name, getattr(self, name))
                                for name in ("modifier", "age", "length", "hashName", "charset")]) + " ]"
class GeneratorException(common.PYMKException):
    pass

class Generator:
    def __init__(self, transcoding):
        self.transcoding = transcoding
    def generate(self, masterPassword, generatorSettings):
        assert isinstance(generatorSettings, GeneratorSettings)
        assert isinstance(generatorSettings.age, int)
        buff = masterPassword + generatorSettings.modifier + str(generatorSettings.age)
        try:
            result = getattr(hashes, generatorSettings.hashName.lower())(buff)
        except AttributeError:
            raise GeneratorException("Hash %r not found" % generatorSettings.hashName)
        result = self.transcoding.transcode(result.digest(), generatorSettings.charset)[:generatorSettings.length]
        return result

generator = Generator(Transcoding())
